package com.example.boot.controller;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.boot.security.sms.SmsCodeAuthenticationProvider;
import com.example.boot.security.sms.SmsCodeAuthenticationToken;
import com.example.common.LogAnno;
import com.example.common.Result;
import com.example.common.enums.*;
import com.example.common.exception.CustomException;
import com.example.constant.JwtClaimsConstant;
import com.example.context.BaseContext;
import com.example.mapper.RoleMapper;
import com.example.mapper.UserAndRoleMapper;
import com.example.pojo.dto.AdminDTO;
import com.example.pojo.dto.ValidDTO;
import com.example.pojo.entity.Role;
import com.example.pojo.entity.UserAndRole;
import com.example.pojo.utils.LoginUser;
import com.example.pojo.utils.Roles;
import com.example.pojo.entity.Account;
import com.example.pojo.entity.Admin;
import com.example.pojo.vo.UserNameVO;
import com.example.properties.JwtProperties;
import com.example.service.AdminService;
import com.example.service.TeamService;
import com.example.service.WebService;
import com.example.utils.BCryptPasswordEncoderUtils;
import com.example.utils.JwtUtil;
import com.example.utils.SmsUtils;
import com.example.utils.ValidateCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 基础前端接口
 */
@RestController
@Slf4j
public class WebController {

    @Resource
    private AdminService adminService;

    @Autowired
    private TeamService teamService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserAndRoleMapper userAndRoleMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private WebService webService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private SmsCodeAuthenticationProvider smsCodeAuthenticationProvider;

    /**
     * 登录
     */
    @LogAnno(title = "登录", type = LogType.LOGIN)
    @PostMapping("/login")
    @Transactional
    public Result login(@RequestBody Admin admin) {
        if (ObjectUtil.isEmpty(admin.getUsername()) || ObjectUtil.isEmpty(admin.getPassword())) {
            return Result.error(ResultCodeEnum.PARAM_LOST_ERROR);
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        Admin dbAdmin = adminService.getOne(new LambdaQueryWrapper<Admin>().eq(Admin::getUsername, admin.getUsername()));
        if (!bCryptPasswordEncoder.matches(admin.getPassword(), dbAdmin.getPassword())){
            return Result.error(ResultCodeEnum.USER_ACCOUNT_ERROR);
        }

        admin = adminService.login(admin);
        return Result.success(admin);
    }

    /**
     * 角色权限
     */
    @RequestMapping("/getPermCode")
    public Result getPermCode() {

        int[] codes = new int[3];
        codes[0] = 1000;
        codes[1] = 1000;
        codes[2] = 1000;
        return Result.success(codes);
    }

    /**
     * 获得当前登录用户信息
     */
    @GetMapping("/findUserName")
    public Result getUserinfo() {
        Admin currentUser = adminService.selectById(Math.toIntExact(BaseContext.getCurrentId()));

        Role role = roleMapper.selectById(userAndRoleMapper
                .selectOne(new LambdaQueryWrapper<UserAndRole>()
                        .eq(UserAndRole::getUserId, currentUser.getId())).getRoleId());

        Roles roles = Roles.builder()
                .roleName(role.getRoleValue())
                .value("super")
                .build();
        String teamName = null;
        Integer teamId = null;
        Admin admin = adminService.selectById(currentUser.getId());
        if (admin.getTeamId() != null){
            teamName = admin.getTeamName();
            teamId = admin.getTeamId();
        }
        UserNameVO userNameVO = UserNameVO.builder()
                .avatar(currentUser.getAvatar())
                .realName(currentUser.getName())
                .username(currentUser.getUsername())
                .teamName(teamName)
                .teamId(teamId)
                .userId(currentUser.getId())
                .phone(currentUser.getPhone())
                .email(currentUser.getEmail())
                .desc(currentUser.getDescription())
                .address(currentUser.getAddress())
                .roles(roles)
                .build();
        return Result.success(userNameVO);
    }

    @GetMapping("/logout")
    public Result logout(){
        return Result.success();
    }

    @GetMapping("/getInfor/{id}")
    public Result getInfoById(@PathVariable Integer id){
        Admin admin = adminService.selectById(Math.toIntExact(BaseContext.getCurrentId()));
        return Result.success(adminService.selectById(id));
    }

    @PutMapping("/update")
    @LogAnno(title = "用户管理", type = LogType.UPDATE)
    public Result updateInfo(@RequestBody Account account){
        Admin admin = new Admin();
        BeanUtils.copyProperties(account,admin);
        adminService.updateAdmin(admin);
        return Result.success();
    }


    /**
     * 注册
     */
    @LogAnno(title = "用户管理", type = LogType.REGISTER)
    @PostMapping("/register")
    public Result register(@RequestBody AdminDTO adminDTO) {
        if (!adminDTO.getConfirmPassword().equals(adminDTO.getPassword())){
            return Result.error(ResultCodeEnum.PASSWORD_NOT_SAME);
        }
        Admin dbAdmin = adminService.getOne(new LambdaQueryWrapper<Admin>()
                .eq(Admin::getUsername, adminDTO.getAccount()));
        if (ObjectUtil.isNotEmpty(dbAdmin)) {
            return Result.error(ResultCodeEnum.USER_EXIST_ERROR);
        }

        String valid = adminDTO.getSms();
        String code = stringRedisTemplate.opsForValue().get(KeyEnum.VALID_KEY + "_" + adminDTO.getMobile() + "_" + valid);
        if (!StringUtils.hasLength(code)){
            return Result.error(ResultCodeEnum.SMS_EXPIRE_ERROR);
        }
        if (!valid.equals(code)){
            return Result.error(ResultCodeEnum.SMS_VALID_ERROR);
        }
        if (ObjectUtil.isNotEmpty(adminService.getOne(new LambdaQueryWrapper<Admin>().eq(Admin::getPhone, adminDTO.getMobile())))){
            return Result.error(ResultCodeEnum.PHONE_EXIST_ERROR);
        }

        Admin admin = Admin.builder()
                .username(adminDTO.getAccount())
                .password(BCryptPasswordEncoderUtils.encodePassword(adminDTO.getPassword()))
                .phone(adminDTO.getMobile())
                .build();

        adminService.save(admin);

        return Result.success();
    }

    /**
     * 阿里云SMS短信服务
     * 发送短信
     */
    @GetMapping("/sendSms/{mobile}")
    public Result sendSms(@PathVariable String mobile) {
        log.info("手机号{} 发送验证码", mobile);
        if (StringUtils.hasLength(mobile)) {
            //从redis中获取短信 查看是否已经重发发送短信
            String codeInRedis = stringRedisTemplate.opsForValue().get(KeyEnum.VALID_KEY + mobile);
            if (StringUtils.hasLength(codeInRedis)) {
                throw new CustomException(ResultCodeEnum.SMS_SAME_ERROR);
            }

            try{
                //获取4位验证码
                String code = ValidateCodeUtils.generateValidateCode(4) + "";
                //发送短信
                webService.sendSms(SmsUtils.VALIDATE_CODE, mobile, code);

                //一分钟内不重复获取
                stringRedisTemplate.opsForValue().set(KeyEnum.VALID_KEY + "_" + mobile, code+"", 1, TimeUnit.MINUTES);
                //五分钟内有效
                stringRedisTemplate.opsForValue().set(KeyEnum.VALID_KEY + "_" + mobile + "_" + code+"", code, 5, TimeUnit.MINUTES);
                return Result.success();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        return Result.error(ResultCodeEnum.SMS_SEND_ERROR);
    }

    /**
     * 手机登录
     */
    @PostMapping("/validSms")
    public Result validSms(@RequestBody ValidDTO validDTO){
        if (ObjectUtil.isEmpty(validDTO)) {
            throw new CustomException(ResultCodeEnum.PARAM_ERROR);
        }
        String mobile = validDTO.getMobile();
        if (!mobile.matches("1[3-9]\\d{9}")) {
            throw new CustomException(ResultCodeEnum.PHONE_ERROR);
        }
        String valid = validDTO.getSms();
        String code = stringRedisTemplate.opsForValue().get(KeyEnum.VALID_KEY + "_" + mobile + "_" + valid);
        if (valid.equals(code)){
            SmsCodeAuthenticationToken smsCodeAuthenticationToken = new SmsCodeAuthenticationToken(mobile, valid);
            Authentication authentication = authenticationManager.authenticate(smsCodeAuthenticationToken);
            if (ObjectUtil.isNull(authentication)){
                throw new CustomException(ResultCodeEnum.PARAM_ERROR);
            }

//            LoginUser loginUser = (LoginUser)smsCodeAuthenticationProvider.loadUserByPhone(validDTO.getMobile());
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            Integer userId = loginUser.getAdmin().getId();
            Map<String, Object> claims = new HashMap<>();
            claims.put(JwtClaimsConstant.ADMIN_ID, userId);
            claims.put(JwtClaimsConstant.USERNAME, loginUser.getUsername());
            String token = JwtUtil.createJWT(
                    jwtProperties.getAdminSecretKey(),
                    jwtProperties.getAdminTtl(),
                    claims
            );

            Admin dbAdmin = loginUser.getAdmin();
            dbAdmin.setToken(token);

            //认证通过 存入redis
            redisTemplate.opsForValue().set(KeyEnum.TOKEN + "_" + userId, JSONUtil.toJsonStr((dbAdmin)));
            redisTemplate.expire(KeyEnum.TOKEN + "_" + userId, 4, TimeUnit.HOURS);
            return Result.success(dbAdmin);

        }
        return Result.error(ResultCodeEnum.SMS_EXPIRE_ERROR);
    }

}
